/*
*author:hlf 
*time:2010-8-8 0:53:56
*/ 
#include "server.h"      

int password_callback(char *buf,int size,int rwflag,void * userdata){
    printf("Call back function called\n");
    strcpy(buf,"hlf0626"); 	
 	  return strlen(buf);
} 
int verify_callbackf(int n,X509_STORE_CTX * x){
	  printf("^_^ Call verify_callbackf function called %d\n",n);
	  return n;
}
int SSL_CTX_set_params(){
  SSL_METHOD *meth= TLSv1_server_method();  
  
    ctx = SSL_CTX_new (meth);
    CHK_NULL(ctx);
    int (*callback)(char *,int ,int ,void *)=&password_callback;
    SSL_CTX_set_default_passwd_cb(ctx,callback); 
	  
    //SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER,NULL);
  
    int (*verify_callback)(int,X509_STORE_CTX *)=&verify_callbackf;
    SSL_CTX_set_verify(ctx,SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,verify_callback);  
  
     SSL_CTX_load_verify_locations(ctx,CACERT,NULL); 
    if (SSL_CTX_use_certificate_file(ctx, CERTF, SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stderr);
        exit(3);
    }
    if (SSL_CTX_use_PrivateKey_file(ctx, KEYF, SSL_FILETYPE_PEM) <= 0) {
        ERR_print_errors_fp(stderr);
        exit(4);
    }
 
    if (!SSL_CTX_check_private_key(ctx)) {
        printf("Private key does not match the certificate public key\n");
        exit(5);
    }
 
    SSL_CTX_set_cipher_list(ctx,"RC4-MD5");

}

SSL* SSL_set_sd(int sd){
	SSL*ssl = SSL_new (ctx);                          
    SSL_set_fd (ssl, sd);
    printf("OK");
    int err = SSL_accept (ssl);
    if(err<=0){
        printf("ssl accept Err.");
        return NULL;
    }
    printf("SSL_accept finished\n");
    if(ssl)
        printf ("SSL connection using %s\n", SSL_get_cipher (ssl)); 
    return ssl;
}
int verify_certificate(SSL * ssl){
    if(SSL_get_verify_result(ssl)!=X509_V_OK){
   	    printf("cli cert invalid.\n");
     	return -1;
    }else{
        printf("cli cert is valid.\n");
    }

    char*    str;
    X509* client_cert = SSL_get_peer_certificate (ssl);
    if (client_cert != NULL) {
        printf ("Client certificate:\n");
   
        str = X509_NAME_oneline (X509_get_subject_name (client_cert), 0, 0);
        printf ("\t subject: %s\n", str);
        free (str);
   
        str = X509_NAME_oneline (X509_get_issuer_name  (client_cert), 0, 0);
        printf ("\t issuer: %s\n", str);
        free (str);
   
        X509_free (client_cert);
    }else {
        printf ("Client does not have certificate.\n");
        return -1;
    }
   
    return 0;    
}


void SSL_init(){
    SSL_library_init();
    SSL_load_error_strings();
    ERR_load_BIO_strings();
    ERR_load_SSL_strings();
    OpenSSL_add_all_algorithms();
}

int tcp_listen(){
	
#if defined(__MINGW32__)
  WSADATA wsaData;
  if(WSAStartup(MAKEWORD(2,2),&wsaData) != 0){
    printf("WSAStartup()fail:%d\n",GetLastError());
    return -1;
  }
#endif
  int err;
  struct sockaddr_in sa_serv;
  printf("Begin TCP socket...\n");

  int listen_sd = socket (AF_INET, SOCK_STREAM, 0);  
  if(listen_sd<=0){
      printf("new Socket Err.");
      return -1;
  }
 
  memset (&sa_serv, '\0', sizeof(sa_serv));
  sa_serv.sin_family      = AF_INET;
  sa_serv.sin_addr.s_addr = INADDR_ANY;
  sa_serv.sin_port        = htons (PORT);         
 
  err = bind(listen_sd, (struct sockaddr*) &sa_serv,sizeof (sa_serv));
  if(err<0){
      printf("bind Socket Err.");
      return -1;
  }
        
  err = listen (listen_sd, 5);                   
    if(err<0){
      printf("listen Socket Err.");
      return -1;
  }
  return listen_sd;
}

int tcp_accept(int listen_sd){
	struct sockaddr_in sa_cli;
    int client_len = sizeof(sa_cli);
    int sd = accept (listen_sd, (struct sockaddr*) &sa_cli, &client_len);
    if(sd<=0){
        printf("accept cli Socket Err.");
        return -1;
    }
 
    printf ("Connection from %lx, port %d\n", sa_cli.sin_addr.s_addr, sa_cli.sin_port);
    return sd;
}
void ssl_read_write(SSL *ssl,int sd){
    char     buf [4096],buf2[4096];
    while(1){ 
        int err = SSL_read (ssl, buf, sizeof(buf) - 1);     
        if(err<=0) 
            break;
        CHK_SSL(err);
        buf[err] = '\0';
        printf ("Client: %s\n", buf);
        if(strcmp(buf,"quit")==0) {
  	        err = SSL_write (ssl,buf, strlen(buf)); 
            break;  
        }
        sprintf(buf2,"the data from client:%slength: %d \n",buf,strlen(buf));
        err = SSL_write (ssl, buf2,strlen(buf2)); 
        CHK_SSL(err);
 
    }

    shutdown (sd,2);
  
}
 

void *ssl_ser_cli_thread(void *arg){
    int sd=(int)*(int *)arg;
    printf("Begin server side SSL\n");
 
    SSL*ssl = SSL_set_sd(sd);      
    if(verify_certificate(ssl)==-1){
  	    printf("SSL verify cert Err.\n");
  	    exit(-1);
    }
    ssl_read_write(ssl,sd);
  
    SSL_free (ssl);
}
static void 
sigpipe_handle(int x){
	 printf("#######signal %d",x);
}

int 
main (){
    //signal(SIGTERM, sigpipe_handle); // kill
	//signal(SIGHUP, sigpipe_handle); // kill -HUP  /  xterm closed

	//signal(SIGINT, sigpipe_handle); // Interrupt from keyboard

	//signal(SIGQUIT, sigpipe_handle); // Quit from keyboard
	signal(SIGPIPE, sigpipe_handle); // Some window managers cause this
	
	//signal(2, sigpipe_handle);
    SSL_init();
    SSL_CTX_set_params();

    int listen_sd;
    if((listen_sd=tcp_listen())==-1){
  	    printf("tcp listen Err.\n");
  	    exit(-1);
    }

    while(1){
        int sd;
        if((sd=tcp_accept(listen_sd))==-1){
     	    printf("tcp listen Err.\n");
    	    exit(-1);
        }
        pthread_t  ntid;
        //int err = pthread_create(&ntid,NULL,ssl_ser_cli_thread,&sd);
     
        ssl_ser_cli_thread(&sd);
    } 
   
    closesocket (listen_sd);
    SSL_CTX_free (ctx);
    return 0;
}
